// ReadCertificates.cpp : Defines the entry point for the console application.
//

//#include "stdafx.h"
#pragma comment(lib, "crypt32.lib")

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wincrypt.h>

#define MS_SCARD_PROV_			L"Microsoft Base Smart Card Crypto Provider"
#define MS_SCARD_CONT_			L"Sign Key Container"
//#define MS_SCARD_CONT_			L"Auth Key Container"
//-------------------------------------------------------------------
// This example uses the function MyHandleError, a simple error
// handling function to print an error message and exit 
// the program. 
// For most applications, replace this function with one 
// that does more extensive error reporting.

void MyHandleError(LPTSTR psz)
{
	_ftprintf(stderr, TEXT("An error occurred in the program. \n"));
	_ftprintf(stderr, TEXT("%s\n"), psz);
	_ftprintf(stderr, TEXT("Error number %x.\n"), GetLastError());
	_ftprintf(stderr, TEXT("Program terminating. \n"));
	exit(1);
} // End of MyHandleError.

void    hexPrintFromByteArray(size_t p, BYTE array[], size_t sz)
{
	size_t i;
	for(i = 0; i < sz; i++)
	{
		if ((i == p) && (p != 0)) printf("\n");
		printf("%02X ", array[i]);
	}
	printf("\n");
} // End of hexPrintFromByteArray.


bool calculateHashOperation(HCRYPTPROV hCryptProv, HCRYPTHASH &hHash, BYTE *pbBuffer, DWORD dwBufferLen)
{
	//-------------------------------------------------------------------
	// Create the hash object.

	if(!CryptCreateHash(
		hCryptProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash))		// out, The address to which the function copies a handle to the new hash object.
		MyHandleError(TEXT("Error during CryptCreateHash."));

	//-------------------------------------------------------------------
	// Compute the cryptographic hash of the buffer.

	if(!CryptHashData(
		hHash,			// Handle of the hash object.
		pbBuffer,		// A pointer to a buffer that contains the data to be added to the hash object.
		dwBufferLen,	// Number of bytes of data to be added.
		0))
		MyHandleError(TEXT("Error during CryptHashData."));

	BYTE        *pbHash;
	DWORD		dwHashLen;
	DWORD       dwHashLenSize	= sizeof(DWORD);

	if(!CryptGetHashParam(
		hHash,
		HP_HASHSIZE,
		(BYTE *)&dwHashLen,
		&dwHashLenSize,
		0))
		MyHandleError(TEXT("CryptGetHashParam failed to get size."));

	pbHash = (BYTE*)malloc(dwHashLen+1);
	if(pbHash == 0)
		MyHandleError(TEXT("Allocation failed."));

	if(CryptGetHashParam(
		hHash,
		HP_HASHVAL,
		pbHash,
		&dwHashLen,
		0))
	{
		// Print the hash value.
		hexPrintFromByteArray(0, pbHash, dwHashLen);
		return true;
	}
	else
	{
		MyHandleError(TEXT("Error during reading hash value."));
		return false;
	}
}	// end of calculateHashOperations

void	readOperations(HCRYPTPROV hCryptProv, CHAR *pszName)
{
	DWORD cbName;
	//---------------------------------------------------------------
	// Read the name of the SmartCard Reader Name.
	
	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_SMARTCARD_READER, 
		(BYTE*)pszName,		// out
		&cbName,			// inout, A pointer to a DWORD value that specifies the size, in bytes, of the buffer pointed to by the pszName parameter
		0))
	{
		printf("SmartCard Reader Name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading SmartCard Reader Name.\n"));
	}

	//---------------------------------------------------------------
	// Read the name of the CSP.
	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_NAME, 
		(BYTE*)pszName,		// out
		&cbName,			// inout, A pointer to a DWORD value that specifies the size, in bytes, of the buffer pointed to by the pszName parameter
		0))
	{
		printf("Provider name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading CSP name.\n"));
	}

	//---------------------------------------------------------------
	// Read the name of the key container.
	cbName = 1000;
	if(CryptGetProvParam(
		hCryptProv, 
		PP_UNIQUE_CONTAINER,	// The CryptAcquireContext function must work with this container name.
		(BYTE*)pszName, 
		&cbName, 
		0))
	{
		printf("Unique Key Container name: %s\n", pszName);
	}
	else
	{
		MyHandleError(TEXT("Error reading key container name.\n"));
	}
}	// end of readOperations

int _tmain(int argc, _TCHAR* argv[])
{
	//---------------------------------------------------------------
	// Declare and initialize variables.
	HCRYPTPROV hCryptProv;

	//---------------------------------------------------------------
	// Get a handle to the default PROV_RSA_FULL provider.
	/*
	Auth Key Container
	Microsoft Base Smart Card Crypto Provider
	Gemplus USB SmartCard Reader 0
	"\\.\<Reader Name>\<Container Name>"
	"\\.\\Gemplus USB SmartCard Reader 0\\Auth Key Container"
	*/
	if(!CryptAcquireContext(
		&hCryptProv,	// out, handle of a cryptographic service provider (CSP)
		0,				//MS_SCARD_CONT_,	// When pszContainer is zero, a default key container name is used
		MS_SCARD_PROV,	//_,				// "Microsoft Base Smart Card Cryptographic Provider"
		PROV_RSA_FULL, 
		0))
		MyHandleError(TEXT("CryptAcquireContext failed.\n"));

	//---------------------------------------------------------------
	// Read of parameters

	CHAR pszName[1000];
	readOperations(hCryptProv, pszName);

	//---------------------------------------------------------------
	// Read containers
	BYTE *ptr = NULL;
	DWORD i;
	BYTE pbData[1000]; // Often allocated dynamically.
	DWORD dwDataLen;
	DWORD dwFlags;
	CHAR *pszAlgType = NULL;
	// Enumerate the containers.
	for(i=0 ; ; i++) {
		// Set the CRYPT_FIRST flag the first time through the loop.
		if(i == 0)	dwFlags = CRYPT_FIRST;
		else		dwFlags = CRYPT_NEXT;

		// Retrieve information about an container.
		dwDataLen = 1000;

		if(!CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, pbData, &dwDataLen, dwFlags)) {
			if(GetLastError() == ERROR_NO_MORE_ITEMS) {
				// Exit the loop.
				printf("i = %d\n", i);		
				break;
			} else {
				printf("Error %x reading algorithm!\n", GetLastError());
				return 0;
			}			
		}
		printf("i = %d - %s\n", i, pbData);		
	}

	//-------------------------------------------------------------------
	// A cryptographic context and a key container are available.
	// Perform any functions that require a cryptographic provider handle.

	//-------------------------------------------------------------------
	// When the handle is no longer needed, it must be released.

	//-------------------------------------------------------------------
	// Declare and initialize variables.

	//HCRYPTPROV hProv;
	BYTE		*pbBuffer		= (BYTE *)"The data that is to be hashed and signed.";
	DWORD		dwBufferLen		= strlen((char *)pbBuffer)+1;	// Null terminated
	HCRYPTHASH	hHash;
	HCRYPTKEY	hKey;
	HCRYPTKEY	hPubKey;
	BYTE		*pbKeyBlob;        
	BYTE		*pbSignature;
	DWORD		dwSigLen;
	DWORD		dwBlobLen;
	DWORD		dwKeySpec		= AT_KEYEXCHANGE;	// Identifies the private key to use from the key container. It can be AT_KEYEXCHANGE or AT_SIGNATURE.
													// #define AT_KEYEXCHANGE          1
													// #define AT_SIGNATURE            2

	//-------------------------------------------------------------------
	/*
	Get the public at signature key.
	This is the public key that will be used by the receiver of the hash to verify the signature.
	In situations where the receiver could obtain the sender's public key from a certificate,
	this step would not be needed.
	*/

	if(!CryptGetUserKey(   
		hCryptProv,	//AT_SIGNATURE,
		dwKeySpec,    
		&hKey))		// out, A pointer to the HCRYPTKEY handle of the retrieved keys
		MyHandleError(TEXT("Error during CryptGetUserKey for signkey."));	// Error # 8009000d AT_SIGNATURE

	//-------------------------------------------------------------------
	// Export the public key. Here the public key is exported to a PUBLICKEYBLOB
	// so that the receiver of the signed hash can verify the signature.
	// This BLOB could be written to a file and sent to another user.

	if(CryptExportKey(   
		hKey,    
		NULL,    
		PUBLICKEYBLOB,
		0,    
		NULL,			// out, pbData
		&dwBlobLen))	// inout, When the function returns, this value contains the number of bytes stored in the buffer pointed to by the pbData parameter.
	{
		printf("Size of the BLOB for the public key determined. %lu \n", dwBlobLen);
	}
	else
	{
		MyHandleError(TEXT("Error computing BLOB length."));
	}

	//-------------------------------------------------------------------
	// Allocate memory for the pbKeyBlob.
	pbKeyBlob = (BYTE*)malloc(dwBlobLen);
	if(pbKeyBlob == 0)
		MyHandleError(TEXT("Out of memory. \n"));

	//-------------------------------------------------------------------
	// Do the actual exporting into the key BLOB.

	if(!CryptExportKey(   
		hKey, 
		NULL,    
		PUBLICKEYBLOB,    
		0,    
		pbKeyBlob,    
		&dwBlobLen))
		MyHandleError(TEXT("Error during CryptExportKey."));

	if(!calculateHashOperation(hCryptProv, hHash, pbBuffer, dwBufferLen))	return -1;

	//-------------------------------------------------------------------
	// Determine the size of the signature and allocate memory.

	dwSigLen= 0;
	if(CryptSignHash(
		hHash, 
		dwKeySpec,	// Identifies the private key to use from the provider's container. It can be AT_KEYEXCHANGE or AT_SIGNATURE.
		//AT_SIGNATURE, 
		NULL, 
		0, 
		NULL, 
		&dwSigLen))		//inout, When the function returns, the DWORD value contains the number of bytes stored in the buffer.
	{
		printf("Signature length %d found.\n",dwSigLen);
	}
	else
	{
		MyHandleError(TEXT("Error during CryptSignHash."));
	}

	//-------------------------------------------------------------------
	// Allocate memory for the signature buffer.

	pbSignature = (BYTE *)malloc(dwSigLen);
	if(pbSignature == 0)
		MyHandleError(TEXT("Out of memory."));

	//-------------------------------------------------------------------
	// Sign the hash object. key's pin is required

	if(!CryptSignHash(
		hHash, 
		dwKeySpec,
		//AT_SIGNATURE, 
		NULL, 
		0, 
		pbSignature, 
		&dwSigLen))
		MyHandleError(TEXT("Error during CryptSignHash !!!"));

	//-------------------------------------------------------------------
	// Destroy the hash object.

	if(hHash) 
		CryptDestroyHash(hHash);

	printf("The signing phase of this program is completed.\n\n");

	//-------------------------------------------------------------------
	// Get the public key of the user who created the digital signature 
	// and import it into the CSP by using CryptImportKey. 
	// This returns a handle to the public key in hPubKey.

	if(!CryptImportKey(
		hCryptProv,
		pbKeyBlob,
		dwBlobLen,
		0,
		0,
		&hPubKey))
		MyHandleError(TEXT("Public key import failed."));

	//-------------------------------------------------------------------
	// Create a new hash object.

	if(!calculateHashOperation(hCryptProv, hHash, pbBuffer, dwBufferLen))	return -1;

	//-------------------------------------------------------------------
	// Validate the digital signature.

	if(CryptVerifySignature(
		hHash,			// A handle to the hash object to verify.
		pbSignature,	// The address of the signature data to be verified.
		dwSigLen,		// The number of bytes in the pbSignature signature data.
		hPubKey,		// A handle to the public key to use to authenticate the signature
		NULL, 
		0)) 
	{
		printf("The signature has been verified.\n");
	}
	else
	{
		printf("Signature not validated!\n");
	}

	//-------------------------------------------------------------------
	// Free memory to be used to store signature.

	if(pbSignature)
		free(pbSignature);

	//-------------------------------------------------------------------
	// Destroy the hash object.

	if(hHash) 
		CryptDestroyHash(hHash);

	//-------------------------------------------------------------------
	// Release the provider handle.

	if (!CryptReleaseContext(hCryptProv,0))
		printf("The handle could not be released.\n");

	return 0;
}